home *** CD-ROM | disk | FTP | other *** search
/ Just Call Me Internet / Just Call Me Internet.iso / prog / atari / c / snz128s / src / article.c < prev    next >
C/C++ Source or Header  |  1994-05-23  |  29KB  |  1,198 lines

  1. /*
  2.     SNEWS 2.0
  3.  
  4.     article- routines to read and display an article
  5.  
  6.  
  7.     Copyright (C) 1991  John McCombs, Christchurch, NEW ZEALAND
  8.                         john@ahuriri.gen.nz
  9.                         PO Box 2708, Christchurch, NEW ZEALAND
  10.  
  11.     This program is free software; you can redistribute it and/or modify
  12.     it under the terms of the GNU General Public License, version 1, as
  13.     published by the Free Software Foundation.
  14.  
  15.     This program is distributed in the hope that it will be useful,
  16.     but WITHOUT ANY WARRANTY; without even the implied warranty of
  17.     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  18.     GNU General Public License for more details.
  19.  
  20.     See the file COPYING, which contains a copy of the GNU General
  21.     Public License.
  22.  
  23.     Atari Version ported by Graham Judd - gjudd@siward.demon.co.uk
  24.  */
  25.  
  26. /*---------------------------- Source Control ------------------------------*/
  27.  
  28. /*
  29.  * $Id: ARTICLE.C,v 1.2 1994/02/05 18:46:56 gbj Exp user $
  30.  */
  31.  
  32. /****************************************************************************
  33. *   22 May 92   1.2     GT  Fix "mail" and "reply" for ka9q.                *
  34. *   31 May 92   1.3     GT      Fix "Reply-To:" line.                       *
  35. *   03 Jun 92   1.4     GT  Less vigorous quoting.                          *
  36. *   05 Jun 92   1.5     GT  Fix author name parsing.                        *
  37. *   06 Jun 92   1.6     GT  Invalidate freed pointers.                      *
  38. *                           Bigger buffer in "get_his_stuff".               *
  39. *   08 Jun 92   1.7     GT  FQDN in "Path:".                                *
  40. *   09 Jun 92   1.8     GT  Right and left cursor keys.                     *
  41. *   12 Jun 92   1.9     NJL Half-way implement putting Date on header :-)   *
  42. *                           Add configurable quotes and Organization: line. *
  43. *                           Ensure strtok() accepts tab as well as space.   *
  44. *                           Stop down-arrow going past end of article.      *
  45. *   26 Jun 92   1.10    GT  Fix group name display format.                  *
  46. *   17 Jul 92   1.11    GT  C++ compilation.                                *
  47. *                           Heap debugging.                                 *
  48. *   16 Jul 92   1.12   MSM  Snews 1.9                                       *
  49. *   21 Nov 92   1.13   MSM  Empty Replyto line bug fixed                     *
  50. *   29 Dev 92   1.14   MSM  Prevent null Organisation/Reply-to lines        *
  51. *                           Fix bug in get_his_stuff returning rubbish      *
  52. *                           Add current line count to article display       *
  53. *                           Path: removed from mail messages                *
  54. *   13 Feb 93   1.15   MSM  Permit editing of full outgoing mail messages   *
  55. *                           Enable editing of 'm'ail article to messages    *
  56. *    6 Mar 93   1.16   MSM  Prevent rude abort on 'out of memory' when      *
  57. *                           reading an article.                             *
  58. *    2 Jun 93   1.17   MSM  Snews 2.0                                       *
  59. *   18 Jun 93   1.18   MSM  Heap debugging removed, now using Bounds Check  *
  60. *   28 Jun 93   1.19   MSM  Mail related items moved to mail.c              *
  61. *    7 Aug 93   1.20   MSM  Change commands to be more TIN'ish              *
  62. *                           Add new command functions                       *
  63. *                           Expert mode                                     *
  64. *   26 Sep 93   1.21   MSM  Colour support clean up, Expert toggle, bugs    *
  65. *    3 Oct 93   1.22   MSM  Case sensitivity of y/n questions removed       *
  66. *                           Old style tab action.                           *
  67. *                           Wide / long screen support corrected            *
  68. *   25 Nov 93   1.23   MSM  Minor header display changes                    *
  69. *    5 Dec 93   1.24   MSM  Followup subject changed                        *
  70. *    2 Apr 94    1.25   MSM  Correct 'K' prompt in non-expert mode        *
  71. *                Append/Replace/Exit options for save        *
  72. *                Article display wrapping at col x-1            *
  73. ****************************************************************************/
  74.  
  75.  
  76. #include "defs.h"
  77. #include "snews.h"
  78. #include "screen.h"
  79. #include "locking.h"
  80.  
  81. #include <io.h>
  82. #include <ctype.h>
  83.  
  84. #ifdef ATARI
  85. #    include "st.h"
  86. #    include "fileops.h"
  87. #endif
  88.  
  89. #ifndef __TURBOC__
  90. #ifndef ATARI
  91. unsigned long farcoreleft(void);
  92. unsigned long testcoreleft(void);
  93. #define BLACK 0
  94. #define LIGHTGRAY 7
  95. #endif
  96. #endif
  97.  
  98.  
  99. int             xfile2 = FALSE;
  100.  
  101. extern char save_name[];
  102.  
  103. char *empty_line = "\n";
  104.  
  105. /*------------------------- read in an article -----------------------------*/
  106. TEXT           *load_article(char *fnx, long offset, int warn)
  107. {
  108.  
  109.     /*
  110.      * Open the file and read it.  Save the author and organisation fill in the structures
  111.      */
  112.  
  113.     FILE           *tmp_file;
  114.     char            buf[BUFSIZ], lnbuf[BUFSIZ], hbuf[BUFSIZ], *p;
  115.     TEXT           *tx;
  116.     LINES          *ln, *lz;
  117.     int             ct, i, flag;
  118.  
  119.     tx = NULL;
  120.     ct = 0;
  121.     flag = 0;
  122.  
  123.     if ((tmp_file = fopen(fnx, "rb")) != NULL) {
  124.  
  125.         fseek(tmp_file, offset, SEEK_SET);
  126.  
  127.         tx = (TEXT *) malloc(sizeof(TEXT));
  128.         tx->top = NULL;
  129.         tx->start = NULL;
  130.         tx->subject = NULL;
  131.         tx->follow_up = NULL;
  132.         tx->author = NULL;
  133.         tx->organisation = NULL;
  134.         tx->newsgroup = NULL;
  135.         tx->distribution = NULL;
  136.         tx->references = NULL;
  137.         strcpy(tx->post_date, "");
  138.  
  139.         while (fgets(buf, sizeof(buf), tmp_file) != NULL) {
  140.  
  141.             if (strncmp(buf, "@@@@END\n", 7) == 0)
  142.                 break;
  143. #ifdef ATARI
  144.             if (farcoreleft() < 2048) {
  145. #else
  146. #ifdef __TURBOC__
  147.             if (farcoreleft() < 2048) {
  148. #else
  149.             if (testcoreleft() < 2048) {
  150. #endif
  151. #endif
  152.                 if (warn == 0) {
  153.                     command("One or more articles partially skipped due to low memory");
  154.                 }
  155.                 else {
  156.                     message("Memory Low, only part article available, press any key");
  157.                     getch();
  158.                 }
  159.                 break;
  160.             }
  161.             expand_tabs(buf, sizeof(buf));
  162.  
  163.             /*
  164.              * We now have a line of input.  If the line is too long it
  165.              * is wrapped at spaces, ',' or '!'.  The lines of text are
  166.              * stored in LINES structures
  167.              */
  168.             p = buf;
  169.             while (strlen(p) > 0) {
  170.  
  171.                 strcpy(lnbuf, p);
  172.                 strcpy(hbuf, p);
  173.                 if (hbuf[strlen(hbuf)-1] == '\n')
  174.                     hbuf[strlen(hbuf)-1] = '\0';
  175.                 if (strlen(p) <= scr_cols+1) {
  176.                     strcpy(lnbuf, p);
  177.                     *p = '\x00';
  178.                 }
  179.                 else {
  180.                     p += /*LINELENGTH*/scr_cols-1;
  181.                     for (i = /*LINELENGTH*/scr_cols-1; i > 50; i--) {
  182.                         if ((lnbuf[i] == ' ') || (lnbuf[i] == '!') || (lnbuf[i] == ',')) {
  183.                             if (lnbuf[i] != ' ') {
  184.                                 i++;
  185.                                 p++;
  186.                             }
  187.                             break;
  188.                         }
  189.                         p--;
  190.                     }
  191.                     lnbuf[i] = '\x00';
  192.                 }
  193.  
  194.                 if (lnbuf[strlen(lnbuf) - 1] != '\n')
  195.                     strcat(lnbuf, "\n");
  196.  
  197.                 /* is it the first line - if so int the TEXT structure */
  198.                 if (ct == 0) {
  199.                     ln = (LINES *) malloc(sizeof(LINES));
  200.                     ln->last = NULL;
  201.                     tx->top = ln;
  202.                 }
  203.                 else {
  204.                     lz = ln;
  205.                     ln->next = (LINES *) malloc(sizeof(LINES));
  206.                     ln = ln->next;
  207.                     ln->last = lz;
  208.                 }
  209.                 ln->next = NULL;
  210.  
  211.                 lnbuf[/*LINELENGTH*/scr_cols/* + 1*/] = '\x00';
  212.                 ln->index = ct;
  213.                 if (lnbuf[0] == '\n')
  214.                     ln->data = empty_line;
  215.                 else {
  216.                     ln->data = (char *) malloc(strlen(lnbuf) + 1);
  217.                     strncpy(ln->data, lnbuf, strlen(lnbuf));
  218.                     ln->data[strlen(lnbuf)] = '\0';
  219.                 }
  220.  
  221.                 if ((strlen(lnbuf) == 1) && (flag == 0) && (tx->start == NULL)) {
  222.                     flag = 1;              /* Skip blank line at head */
  223.                     tx->start = ln;          /* just in case there is is no body */
  224.                 }
  225.                 else
  226.                     if (flag == 1) {
  227.                         tx->start = ln;
  228.                         flag = 2;
  229.                     }
  230.                 ct++;
  231.  
  232.                 /* save the header info */
  233.                 if ((tx->start == NULL) && (strnicmp("From:", lnbuf, 5) == 0)) {
  234.                     tx->author = (char *) malloc(strlen(hbuf)-4);
  235.                     strcpy(tx->author, hbuf+6);
  236.                 }
  237.                 if ((tx->start == NULL) &&
  238.                     ((strncmp("Organisation:", lnbuf, 13) == 0) ||
  239.                      (strncmp("Organization:", lnbuf, 13) == 0))) {
  240.                     tx->organisation = (char *) malloc(strlen(hbuf)-12);
  241.                     strcpy(tx->organisation, hbuf+14);
  242.                 }
  243.                 if ((tx->start == NULL) && (strnicmp("Followup-To:", lnbuf, 12) == 0)) {
  244.                     tx->follow_up = (char *) malloc(strlen(hbuf) - 11);
  245.                     strcpy(tx->follow_up, hbuf+13);
  246.                 }
  247.                 if ((tx->start == NULL) && (strnicmp("Newsgroups:", lnbuf, 11) == 0)) {
  248.                     tx->newsgroup = (char *) malloc(strlen(hbuf) - 10);
  249.                     strcpy(tx->newsgroup, hbuf+12);
  250.                 }
  251.                 if ((tx->start == NULL) && (strnicmp("Distribution:", lnbuf, 13) == 0)) {
  252.                     tx->distribution = (char *) malloc(strlen(hbuf) - 12);
  253.                     strcpy(tx->distribution, hbuf+14);
  254.                 }
  255.                 if ((tx->start == NULL) && (strnicmp("References:", lnbuf, 11) == 0)) {
  256.                     tx->references = (char *) malloc(strlen(hbuf) - 10);
  257.                     strcpy(tx->references, hbuf+12);
  258.                 }
  259.                 /* I want to display the article date, but dunno where :-) */
  260.                 if ((tx->start == NULL) && (strnicmp("Date:", lnbuf, 5) == 0)) {
  261.                     strcpy(tx->post_date, lnbuf+6);
  262.                     if (tx->post_date[(strlen(tx->post_date))-1] == '\n')
  263.                         tx->post_date[(strlen(tx->post_date))-1] = '\0';
  264.                 }
  265.                 if ((tx->start == NULL) && (strnicmp("Subject:", lnbuf, 8) == 0)) {
  266.                     tx->subject = (char *) malloc(strlen(hbuf) - 7);
  267.                     strcpy(tx->subject, hbuf + 9);
  268.                 }
  269.             }
  270.  
  271.         }
  272.  
  273.         tx->lines = ct;
  274.  
  275.         fclose(tmp_file);
  276.     }
  277.     return (tx);
  278. }
  279.  
  280.  
  281. /*---------------------- deallocate article memory ------------------------*/
  282. void            free_article(TEXT * t)
  283. {
  284.  
  285.     LINES          *l, *k;
  286.  
  287.     l = t->top;
  288.     while (l != NULL) {
  289.         k = l;
  290.         l = l->next;
  291.         if (k->data != empty_line)
  292.             free(k->data);
  293.         k->data = NULL;
  294.         free(k);
  295.         k = NULL;
  296.     }
  297.  
  298.     if (t->subject)
  299.         free(t->subject);
  300.     if (t->follow_up)
  301.         free(t->follow_up);
  302.     if (t->author)
  303.         free(t->author);
  304.     if (t->organisation)
  305.         free(t->organisation);
  306.     if (t->newsgroup)
  307.         free(t->newsgroup);
  308.     if (t->distribution)
  309.         free(t->distribution);
  310.     if (t->references)
  311.         free(t->references);
  312.     free(t);
  313.     t = NULL;
  314. }
  315.  
  316.  
  317.  
  318. /*---------------------------- read an article ----------------------------*/
  319. int             read_article(ACTIVE * gp, TEXT * tx, ARTICLE *this_thread, int a_ct, ART_ID *id)
  320. {
  321.  
  322.     /*
  323.      * This routine allows the user to read an article
  324.      */
  325.  
  326.     LINES          *this_art, *tmp;          /* current thread */
  327.     int             exit_code;              /* why we are exiting the loop */
  328.     char            sub_tmp[80];
  329.  
  330.     int             ch, i, idx, ch2;
  331.  
  332.     this_art = tx->start;
  333.     exit_code = 0;
  334.     show_article(gp, tx, tx->subject, this_art, a_ct, this_thread->num_articles);
  335.  
  336.     while ((exit_code == 0) || (exit_code == EX_DUMMY)) {
  337.  
  338.         exit_code = 0;
  339.         ch = getch();
  340.         switch (ch) {
  341.  
  342.           case 0:
  343.           case 0xE0:
  344.             ch = getch();
  345.  
  346.             switch (ch) {
  347.  
  348.               case Fn1:
  349.                 show_help(HELP_ARTICLES);
  350.                 break;
  351.  
  352.               case Fn2:
  353.                 show_values();
  354.                 break;
  355.  
  356.               case Fn3:
  357.                 change_values();
  358.                 break;
  359.  
  360.               case UP_ARR:
  361.                 if (this_art->last != NULL) {
  362.                     this_art = this_art->last;
  363.                     gotoxy(1, TEXT_LINE + PAGE_LENGTH - 1);
  364.                     delline();
  365.                     gotoxy(1, TEXT_LINE);
  366.                     insline();
  367.                     clreol();
  368.                     cputs(this_art->data);
  369.                 }
  370.                 exit_code = EX_DUMMY;
  371.                 break;
  372.  
  373.               case DN_ARR:
  374.                 if (this_art->next != NULL) {
  375.                     tmp = this_art;
  376.                     for (i = 0; i < PAGE_LENGTH; i++) {
  377.                         if (tmp == NULL)
  378.                             break;
  379.                         tmp = tmp->next;
  380.                     }
  381.                     if (tmp != NULL) {
  382.                         this_art = this_art->next;
  383.                         gotoxy(1, TEXT_LINE);
  384.                         delline();
  385.                         gotoxy(1, TEXT_LINE + PAGE_LENGTH - 1);
  386.                         insline();
  387.                         clreol();
  388.                         cputs(tmp->data);
  389.                     }
  390.                 }
  391.                 exit_code = EX_DUMMY;
  392.                 break;
  393.  
  394.               case PGUP:
  395.                 if (this_art->last == NULL)
  396.                     exit_code = EX_DUMMY;
  397.                 else
  398.                     for (i = 0; i < PAGE_LENGTH-2; i++) {
  399.                         if (this_art->last == NULL)
  400.                             break;
  401.                         this_art = this_art->last;
  402.                     }
  403.                 break;
  404.  
  405.               case PGDN:
  406.                 if (this_art->next != NULL) {
  407.                     tmp = this_art;
  408.                     for (i = 0; i < PAGE_LENGTH; i++) {
  409.                         tmp = tmp->next;
  410.                         if (tmp == NULL)
  411.                             break;
  412.                     }
  413.                     if (tmp != NULL)
  414.                         this_art = tmp->last->last;
  415.                     else
  416.                         exit_code = EX_DUMMY;
  417.                 }
  418.                 break;
  419.  
  420.               case HOME:
  421.                 this_art = tx->start;
  422.                 break;
  423.  
  424.               case END:
  425.                 while (this_art->next != NULL)
  426.                     this_art = this_art->next;
  427.                 for (i = 0; i < PAGE_LENGTH - 1; i++) {
  428.                     if (this_art->last == NULL)
  429.                         break;
  430.                     this_art = this_art->last;
  431.                 }
  432.                 break;
  433.  
  434.               case LT_ARR:
  435.                 exit_code = (a_ct == 1) ? EX_QUIT : EX_PREV;
  436.                 exit_code |= EX_NOTREAD;
  437.                 break;
  438.  
  439.               case RT_ARR:
  440.                 exit_code = (a_ct == this_thread->num_articles) ? EX_DUMMY : (EX_NEXT | EX_NOTREAD);
  441.                 break;
  442.  
  443.             }
  444.             break;
  445.  
  446.             case   2:
  447.           case  21:
  448.           case 'b':
  449.             if (this_art->last == NULL)
  450.                 exit_code = EX_DUMMY;
  451.             else
  452.                 for (i = 0; i < PAGE_LENGTH-2; i++) {
  453.                     if (this_art->last == NULL)
  454.                         break;
  455.                     this_art = this_art->last;
  456.                 }
  457.             break;
  458.  
  459.           case   4:
  460.           case   6:
  461.           case ' ':
  462.             if (this_art->next != NULL) {
  463.                 tmp = this_art;
  464.                 for (i = 0; i < PAGE_LENGTH; i++) {
  465.                     tmp = tmp->next;
  466.                     if (tmp == NULL)
  467.                         break;
  468.                 }
  469.                 if (tmp != NULL)
  470.                     this_art = tmp->last->last;
  471.                 else
  472.                     exit_code = EX_DUMMY;
  473.             }
  474.             break;
  475.  
  476.           case   8:
  477.             this_art = tx->top;
  478.             break;
  479.  
  480.           case  18: /* ^r */
  481.           case 'g':
  482.             this_art = tx->start;
  483.             break;
  484.  
  485.           case '$':
  486.           case 'G':
  487.             while (this_art->next != NULL)
  488.                 this_art = this_art->next;
  489.             for (i = 0; i < PAGE_LENGTH - 1; i++) {
  490.                 if (this_art->last == NULL)
  491.                     break;
  492.                 this_art = this_art->last;
  493.             }
  494.             break;
  495.  
  496.           case '!':
  497.             textbackground(BLACK);
  498.             textcolor(LIGHTGRAY);
  499.             clrscr();
  500. #ifdef ATARI
  501.             printf("Sorry, Shell function not supported.    \n");
  502.             printf("Press any key to return to Snews.\n");
  503.             getch();
  504. #else
  505.             printf("Type <EXIT> to return to Snews.\n\n");
  506.             system("");
  507. #endif
  508.             textbackground(textb);
  509.             textcolor(textf);
  510.             clrscr();
  511.             break;
  512.  
  513.           case 'v':
  514. #ifdef ATARI
  515.             sprintf(sub_tmp, "Demon Internet Simple News v%02d.%02d.%02d LC5 compiled %s", rmj, rmm, rup, __DATE__);
  516. #else
  517. #ifdef __TURBOC__
  518.             sprintf(sub_tmp, "Demon Internet Simple News v%02d.%02d.%02d BC++ compiled %s", rmj, rmm, rup, __DATE__);
  519. #else
  520.             sprintf(sub_tmp, "Demon Internet Simple News v%02d.%02d.%02d MSVC compiled %s", rmj, rmm, rup, __DATE__);
  521. #endif
  522. #endif
  523.             message(sub_tmp);
  524.             getch();
  525.             break;
  526.  
  527.           case 'w':
  528.               if (gp->suspend == TRUE)
  529.               {
  530.                   message("This group is suspended, post regardless ? (y/n) ");
  531.                   ch2 = getch();
  532.                   ch2 = tolower(ch2);
  533.                   message("");
  534.             }
  535.             else
  536.                 ch2 = 'y';
  537.             if (ch2 != 'y')
  538.                 break;
  539.             strcpy(sub_tmp, "");
  540.             post(NULL, gp->group, sub_tmp);
  541.             break;
  542.  
  543.           case 'f':
  544.           case 'F':
  545.               if (gp->suspend == TRUE)
  546.               {
  547.                   message("This group is suspended, post regardless ? (y/n) ");
  548.                   ch2 = getch();
  549.                   ch2 = tolower(ch2);
  550.                   message("");
  551.             }
  552.             else
  553.                 ch2 = 'y';
  554.             if (ch2 != 'y')
  555.                 break;
  556.             if (tx->follow_up == NULL)
  557.                 post(tx, gp->group, tx->subject);
  558.             else if (strnicmp(tx->follow_up, "poster", 6) == 0) {
  559.                 message("Followup-To is to Poster, mail reply (y/n) - ");
  560.                 do
  561.                     {
  562.                     ch = getch();
  563.                     ch = tolower(ch);
  564.                 }
  565.                 while ((ch != 'y') && (ch != 'n'));
  566.                 if (ch == 'y')
  567.                     reply_to_article(tx, tx->subject);
  568.                 else
  569.                     post(tx, gp->group, tx->subject);
  570.             }
  571.             else {
  572.                 post(tx, tx->follow_up, tx->subject);
  573.             }
  574.             break;
  575.  
  576.           case 'r':
  577.             reply_to_article(tx, this_thread->header);
  578.             break;
  579.  
  580.           case 'R':
  581.             ReplyAddress(tx, this_thread->header);
  582.             break;
  583.  
  584.           case 'm':
  585.             mail_to_someone(tx);
  586.             break;
  587.  
  588.           case 'M':
  589.             mail_to_someone(NULL);
  590.             break;
  591.  
  592.           case 's':
  593.             xfile2 = FALSE;
  594.             save_to_disk(gp, id->art_off);
  595.             message("-- Done --");
  596.             break;
  597.  
  598.           case 'S':
  599.             xfile2 = TRUE;
  600.             save_to_disk(gp, id->art_off);
  601.             xfile2 = FALSE;
  602.             message("-- Done --");
  603.             break;
  604.  
  605.           case 'd':
  606.             rot13(tx);
  607.             break;
  608.  
  609.           case 'o':
  610.             print_article(gp, id->art_off);
  611.             message("-- Done --");
  612.             break;
  613.  
  614.           case 'h':
  615.           case 'H':
  616.             show_help(HELP_ARTICLES);
  617.             break;
  618.  
  619.           case 'K':
  620.             if (mark_thread_as_read(gp, this_thread, FALSE) == TRUE) {
  621.                 exit_code = EX_NEXT_UNREAD;
  622.             }
  623.             break;
  624.  
  625.           case 'N':
  626.             exit_code = EX_NEXT_UNREAD;
  627.             break;
  628.  
  629.           case 'z':
  630.             idx = (int) ((id->id) - gp->lo_num - 1);
  631.             *((gp->read_list) + idx) = (char) FALSE;
  632.             exit_code = (EX_NEXT | EX_NOTREAD);
  633.             break;
  634.  
  635.           case 'n':
  636.           case 'k':
  637.             exit_code = EX_NEXT;
  638.             break;
  639.  
  640.           case '<':
  641.             exit_code = EX_FIRST;
  642.             break;
  643.  
  644.           case '>':
  645.             exit_code = EX_LAST;
  646.             break;
  647.             
  648.           case TAB:
  649.             if (my_stuff.tab_action == TRUE) {
  650.                         if (this_art->next != NULL) {
  651.                             tmp = this_art;
  652.                             for (i=0;i< PAGE_LENGTH; i++) {
  653.                                     tmp = tmp->next;
  654.                                     if (tmp == NULL)
  655.                                         break;
  656.                             }
  657.                             if ((tmp != NULL) && (i > 2)) {
  658.                                     this_art = tmp->last->last;
  659.                         while (tmp != NULL) {
  660.                             if (tmp->data[0] != '\n')
  661.                                 break;
  662.                             tmp = tmp->next;
  663.                         }
  664. #ifdef ATARI
  665.                         if (tmp == NULL)
  666.                             exit_code=EX_NEXT_UNREAD;
  667.                         else if ((tmp->data)[0] == '\n')
  668.                             exit_code = EX_NEXT_UNREAD;
  669. #else
  670.                         if (((tmp->data)[0] == '\n') || (tmp == NULL))
  671.                             exit_code = EX_NEXT_UNREAD;
  672. #endif
  673.                     }
  674.                     else
  675.                         exit_code = EX_NEXT_UNREAD;
  676.                 }
  677.                 else
  678.                    exit_code = EX_NEXT_UNREAD;
  679.                 break;
  680.             }
  681.             else { /* old style tab action */
  682.                 exit_code = EX_NEXT_UNREAD;
  683.                 break;
  684.             }
  685.  
  686.           case ENTER:
  687.             exit_code = EX_NEXT;
  688.             break;
  689.  
  690.           case ESCAPE:
  691.           case 'q':
  692.           case 'Q':
  693.           case 'T':
  694.             exit_code = EX_QUIT;
  695.             exit_code |= EX_NOTREAD;
  696.             break;
  697.  
  698.           case 'p':
  699.             exit_code = EX_PREVIOUS;
  700.             break;
  701.  
  702.           case '+':
  703.           case '=':
  704.           case '/':
  705.             exit_code = EX_SEARCH_FORW;
  706.             exit_code |= EX_NOTREAD;
  707.             break;
  708.  
  709.           case '-':
  710.           case '?':
  711.             exit_code = EX_SEARCH_BACKW;
  712.             exit_code |= EX_NOTREAD;
  713.             break;
  714.  
  715.           case 'B':
  716.             bug_report();
  717.             show_article(gp, tx, tx->subject, this_art, a_ct, this_thread->num_articles);
  718.  
  719.           default:
  720.             exit_code = EX_DUMMY;
  721.             break;
  722.         }
  723.  
  724.         if (exit_code == 0)
  725.             show_article(gp, tx, tx->subject, this_art, a_ct, this_thread->num_articles);
  726.         else {
  727.             gotoxy(scr_cols-16, 2);
  728.             textbackground(headb);
  729.             textcolor(headf);
  730.             cprintf("%4d", this_art->index + 1);
  731.             textbackground(textb);
  732.             textcolor(textf);
  733.         }
  734.     }
  735.  
  736.     return (exit_code);
  737. }
  738.  
  739.  
  740.  
  741. /*--------------------------- show the article ----------------------------*/
  742. void            show_article(ACTIVE * gp, TEXT * tx, char *subject, LINES * this_art, int a_ct,
  743.                                              int of_ct)
  744. {
  745.  
  746.     /*
  747.      * This routine show a page of an article
  748.      */
  749.  
  750.     int             i, j;
  751.     char            buf[512];
  752. #ifdef ATARI
  753.     int alen, olen;
  754. #endif
  755.  
  756.     clrscr();
  757.  
  758.     textbackground(headb);
  759.     textcolor(headf);
  760.  
  761. #ifdef ATARI
  762.     for (i=0; i < 4; i++)
  763.         printf("%*s", scr_cols, " ");
  764. #else
  765.     clreol();
  766. #endif
  767.     if (strnicmp(gp->group, "junk", 4) == 0) {
  768.         if ((strlen(tx->newsgroup) <= 30) && (tx->newsgroup[strlen(tx->newsgroup)-1] == '\n'))
  769.             tx->newsgroup[strlen(tx->newsgroup)-1] = '\0';
  770.         strcpy(buf, "");
  771.         j = 16 - (strlen(tx->newsgroup)/2);
  772.         if (j>1) {
  773.             for (i=0;i<j;i++)
  774.                 strcat(buf, " ");
  775.         }
  776.         strcat(buf, tx->newsgroup);
  777.         gotoxy(1, 1);
  778.         cprintf("%-25.25s", tx->post_date);
  779.         gotoxy((scr_cols/2) - 16, 1);
  780.         cprintf("%-31.31s", buf);
  781.         gotoxy(scr_cols-22, 1);
  782.         cprintf("Article: %4d of %4d\r\n", a_ct, of_ct);
  783.     }
  784.     else {
  785.         strcpy(buf, "");
  786.         j = 16 - (strlen(gp->group)/2);
  787.         if (j>1) {
  788.             for (i=0;i<j;i++)
  789.                 strcat(buf, " ");
  790.         }
  791.         strcat(buf, gp->group);
  792.         gotoxy(1, 1);
  793.         cprintf("%-25.25s", tx->post_date);
  794.         gotoxy((scr_cols/2) - 16, 1);
  795.         cprintf("%-31.31s", buf);
  796.         gotoxy(scr_cols-22, 1);
  797.         cprintf("Article: %4d of %4d\r\n", a_ct, of_ct);
  798.     }
  799. #ifndef ATARI
  800.     clreol();
  801. #endif
  802.     strncpy(buf, subject, 58);
  803.     if ((strlen(buf) <= 49) && (buf[strlen(buf)-1] == '\n'))
  804.         buf[strlen(buf)-1] = '\x00';
  805.     else
  806.         buf[49] = '\x00';
  807.  
  808.     cprintf("Subject: %-50.50s\r\n", buf);
  809.     gotoxy(scr_cols-16, 2);
  810.     cprintf("%4d / %4d lines\r\n", this_art->index + 1, tx->lines);
  811. #ifndef ATARI
  812.     clreol();
  813. #endif
  814.     
  815. #ifdef ATARI
  816.     if (tx->author)
  817.         alen=strlen(tx->author);
  818.     else
  819.         alen=0;
  820.     if (tx->organisation)
  821.         olen=strlen(tx->organisation);
  822.     else
  823.         olen=0;
  824. #endif
  825.  
  826. #ifdef ATARI
  827.     if (alen + olen > (size_t)(scr_cols-10))
  828. #else
  829.     if (strlen(tx->author) + strlen(tx->organisation) > (size_t)(scr_cols-10))
  830. #endif
  831.         {
  832. #ifdef ATARI
  833.         if (alen > olen)
  834. #else
  835.         if (strlen(tx->author) > strlen(tx->organisation))
  836. #endif
  837.             {
  838. #ifdef ATARI
  839.             i = (scr_cols - 10) - olen;
  840. #else
  841.             i = (scr_cols - 10) - strlen(tx->organisation);
  842. #endif
  843.             if (i < (scr_cols-10)/2)
  844.                 i = (scr_cols-10)/2;
  845.             if (tx->author == NULL)
  846.                 strcpy(buf, "Unknown");
  847.             else
  848.                 strncpy(buf, tx->author, i);
  849.             buf[i] = '\0';
  850.             strcat(buf, " at ");
  851.             if (tx->organisation == NULL)
  852.                 strcat(buf, "(none)");
  853.             else
  854.                 strncat(buf, tx->organisation, (scr_cols-10)-i);
  855.             buf[(scr_cols-10)] = '\0';
  856.         }
  857.         else
  858.             {
  859. #ifdef ATARI
  860.             i = (scr_cols-10) - alen;
  861. #else
  862.             i = (scr_cols-10) - strlen(tx->author);
  863. #endif
  864.             if (i < (scr_cols-10)/2)
  865.                 i = (scr_cols-10)/2;
  866.             if (tx->author == NULL)
  867.                 strcpy(buf, "Unknown");
  868.             else
  869.                 strncpy(buf, tx->author, (scr_cols-12)-i);
  870.             buf[(scr_cols-12)-i] = '\0';
  871.             strcat(buf, " at ");
  872.             if (tx->organisation == NULL)
  873.                 strcat(buf, "(none)");
  874.             else
  875.                 strncat(buf, tx->organisation, i);
  876.             buf[(scr_cols-10)] = '\0';
  877.         }
  878.     }
  879.     else
  880.         {
  881.         if (tx->author == NULL)
  882.             strcpy(buf, "Unknown");
  883.         else
  884.             strcpy(buf, tx->author);
  885.         strcat(buf, " at ");
  886.         if (tx->organisation == NULL) {
  887.             if (strlen(buf) > (size_t) scr_cols-17)
  888.                 buf[scr_cols-17] = '\0';
  889.             strcat(buf, "(none)");
  890.         }
  891.         else
  892.             strcat(buf, tx->organisation);
  893.     }
  894.     cprintf("From: %s", buf);
  895.     textbackground(textb);
  896.     textcolor(textf);
  897.  
  898.  
  899.     gotoxy(1, TEXT_LINE);
  900.     for (i = 0; i < PAGE_LENGTH; i++) {
  901.         gotoxy(1, i + TEXT_LINE);
  902.         cputs(this_art->data);
  903.         this_art = this_art->next;
  904.         if (this_art == NULL)
  905.             break;
  906.     }
  907.  
  908.     sprintf(buf, "ESC=select thread   TAB=next  ENTER=next article  F1 or 'h'=help   [%ldk]",
  909.         farcoreleft()/1024);
  910.     command(buf);
  911. }
  912.  
  913.  
  914.  
  915. /*-------------------------- save article --------------------------------*/
  916. void            save_to_disk(ACTIVE *gp, long offset)
  917. {
  918.  
  919.     /*
  920.      * This routine saves an article to disk, appending if necessary
  921.      */
  922.  
  923.     FILE           *tmp = NULL, *tmp_file;
  924.     char            fn[80];
  925.     int             ch;
  926.     char           *fnx;
  927.     time_t          now;
  928.     struct tm      *tmnow;
  929.     char            timestr[64], prompt[128];
  930.     char            buf[512];
  931.  
  932.     if (xfile2 == TRUE) {
  933.         strcpy(fn, my_stuff.mail_dir);
  934.         strcat(fn, my_stuff.extruser);
  935.         strcat(fn, ".txt");
  936.     }
  937.     else {
  938.         sprintf(prompt, "Enter filename? [%s] ", save_name);
  939.         lmessage(prompt);
  940.         gets(fn);
  941.         if (strlen(fn) > 0)
  942.             strcpy(save_name, fn);
  943.         else
  944.             strcpy(fn, save_name);
  945.     }
  946.  
  947.     if (access(fn, 0) == 0) {
  948.         if (xfile2 == TRUE) {
  949.             if ((tmp = fopen(fn, "at")) == NULL) {
  950.                 message("*** Cannot open file for appending - "
  951.                         "press any key to continue ***");
  952.                 getch();
  953.             }
  954.         }
  955.         else {
  956.             message("File exists - append/replace/exit(a/r/e)? ");
  957.             do
  958.                 {
  959.                 ch = getch();
  960.                 ch = tolower(ch);
  961.             }
  962.             while ((ch != 'a') && (ch != 'r') && (ch != 'e'));
  963.             if (ch == 'e') {
  964.                 message("*** Save aborted - press any key to continue ***");
  965.                 getch();
  966.                 return;
  967.             }
  968.             if (ch == 'a') {
  969.                 if ((tmp = fopen(fn, "at")) == NULL) {
  970.                     message("*** Cannot open file for appending - press any key to continue ***");
  971.                     getch();
  972.                 }
  973.             }
  974.             else {
  975.                 if ((tmp = fopen(fn, "wt")) == NULL) {
  976.                     message("*** Cannot open file for output - press any key to continue ***");
  977.                     getch();
  978.                 }
  979.             }
  980.         }
  981.  
  982.     }
  983.     else {
  984.  
  985.         if ((tmp = fopen(fn, "wt")) == NULL) {
  986.             message("*** Cannot open file for output - press a key to continue ***");
  987.             getch();
  988.         }
  989.     }
  990.  
  991.     if (tmp != NULL) {
  992.  
  993.         fnx = make_news_group_name(gp->group);
  994.  
  995.         if (xfile2 == TRUE) {
  996.             tzset();
  997.             time(&now);
  998.             tmnow = localtime(&now);
  999.             strftime(timestr, sizeof(timestr), "%a %b %d %H:%M:%S %Z %Y", tmnow);
  1000.             fprintf(tmp, "From snews@%s.%s %s\n", my_stuff.my_site,
  1001.                     my_stuff.my_domain, timestr);
  1002.         }
  1003.  
  1004.         if ((tmp_file = fopen(fnx, "rb")) != NULL) {
  1005.  
  1006.         fseek(tmp_file, offset, SEEK_SET);
  1007.                while (fgets(buf, sizeof(buf), tmp_file) != NULL) {
  1008.                    if (strncmp(buf, "@@@@END\n", 7) == 0)
  1009.                        break;
  1010.                    fputs(buf, tmp);
  1011.             }
  1012.             fclose(tmp_file);
  1013.         }
  1014.         fclose(tmp);
  1015.     }
  1016. }
  1017.  
  1018. /*-------------------------- print article --------------------------------*/
  1019. void            print_article(ACTIVE *gp, long offset)
  1020. {
  1021.  
  1022.     /*
  1023.      * This routine prints an article
  1024.      */
  1025.  
  1026.     FILE           *tmp_file;
  1027.     char           *fnx;
  1028.     char            buf[512];
  1029.  
  1030.     strcpy(save_name, "prn");
  1031.  
  1032.  
  1033.  
  1034.     fnx = make_news_group_name(gp->group);
  1035.  
  1036.     if ((tmp_file = fopen(fnx, "rb")) != NULL) {
  1037.  
  1038.         fseek(tmp_file, offset, SEEK_SET);
  1039.         while (fgets(buf, sizeof(buf), tmp_file) != NULL) {
  1040.             if (strncmp(buf, "@@@@END\n", 7) == 0)
  1041.                 break;
  1042.             fputs(buf, stdprn);
  1043.             fputc(0x0d, stdprn);
  1044.         }
  1045.         fputc(0x0c, stdprn);
  1046.         fflush(stdprn);
  1047.         fclose(tmp_file);
  1048.     }
  1049.  
  1050. }
  1051.  
  1052. /*----------------------- get stuff off article header --------------------*/
  1053. void            get_his_stuff(TEXT * tx, char *author, char *msg_id, char *r_name)
  1054. {
  1055.  
  1056.     /*
  1057.      * Retrieve the author and msg_id from the article
  1058.      */
  1059.  
  1060.     LINES          *ln;
  1061.     char           *p, *q;
  1062.     char            buf[BUFSIZ];
  1063.     char           *null_name = {" ** none ** "};
  1064.  
  1065.     DBGOUT(("%s:%d\n", __FILE__, __LINE__));
  1066.     strcpy(author, null_name);
  1067.     strcpy(msg_id, " <none> ");
  1068.     strcpy(r_name, "");
  1069.  
  1070.     ln = tx->top;
  1071.     while (ln != NULL) {
  1072.         strncpy(buf, ln->data, sizeof(buf));
  1073.         *(buf + sizeof(buf) - 1) = '\0';
  1074.         p = strtok(buf, " \t:\n\r");
  1075.         p = strtok(NULL, " \t:\n\r");
  1076.  
  1077.         if (strnicmp(ln->data, "Message-ID:", 11) == 0) {
  1078.             if (p != NULL)
  1079.                 strcpy(msg_id, p);
  1080.         }
  1081.  
  1082.         if ((strnicmp(ln->data, "From:", 5) == 0) &&
  1083.             (strcmp(author, null_name) == 0)) {
  1084.             if ((q = strchr(ln->data, '<')) != NULL) {
  1085.                 strcpy(buf, ln->data);
  1086.                 q = strchr(buf, '<');
  1087.                 *(q-1) = '\0';
  1088.                 if (*(buf+6) == '\"') {
  1089.                     strcpy(r_name, buf+6);
  1090.                 }
  1091.                 else {
  1092.                     strcpy(r_name, "\"");
  1093.                     strcat(r_name, buf+6);
  1094.                     strcat(r_name, "\"");
  1095.                 }
  1096.                 q++;
  1097.                 p = strtok(q, ">");
  1098.             }
  1099.             if (p != NULL)
  1100.                 strcpy(author, p);
  1101.  
  1102.             p = NULL;
  1103.             if ((q = strchr(ln->data, '(')) != NULL) {
  1104.                 strcpy(buf, ln->data);
  1105.                 q = strchr(buf, '(');
  1106.                 q++;
  1107.                 p = strtok(q, ")");
  1108.                 if (p != NULL) {
  1109.                     if (*p == '\"') {
  1110.                         strcpy(r_name, p);
  1111.                     }
  1112.                     else {
  1113.                         strcpy(r_name, "\"");
  1114.                         strcat(r_name, p);
  1115.                         strcat(r_name, "\"");
  1116.                     }
  1117.                 }
  1118.             }
  1119.         }
  1120.  
  1121.         if (strnicmp(ln->data, "Reply-To:", 9) == 0) {
  1122.             if ((q = strchr(ln->data, '<')) != NULL) {
  1123.                 strcpy(buf, ln->data);
  1124.                 q = strchr(buf, '<');
  1125.                 q++;
  1126.                 p = strtok(q, ">");
  1127.             }
  1128.             if (p != NULL)
  1129.                 strcpy(author, p);
  1130.         }
  1131.  
  1132.         if (strlen(ln->data) < 2)
  1133.             break;
  1134.         ln = ln->next;
  1135.     }
  1136.  
  1137. }
  1138.  
  1139.  
  1140. /*--------------------------- rot 13 the article ------------------------*/
  1141. void            rot13(TEXT * tx)
  1142. {
  1143.     LINES          *ln;
  1144.     int             i, c;
  1145.  
  1146.  
  1147.     ln = tx->start;
  1148.  
  1149.     while (ln != NULL) {
  1150.         for (i = 0; i < (int)(strlen(ln->data)); i++) {
  1151.             c = *((ln->data) + i);
  1152.             if ((c >= 'A') && (c <= 'Z')) {
  1153.                 *((ln->data) + i) = (((c - 'A') + 13) % 26) + 'A';
  1154.             }
  1155.             else {
  1156.                 if ((c >= 'a') && (c <= 'z')) {
  1157.                     *((ln->data) + i) = (((c - 'a') + 13) % 26) + 'a';
  1158.                 }
  1159.             }
  1160.         }
  1161.         ln = ln->next;
  1162.     }
  1163. }
  1164.  
  1165.  
  1166. /*--------------------------- expand the tabs ----------------------------*/
  1167. void            expand_tabs(char *buf, int max_len)
  1168. {
  1169.     int             l, k;
  1170.     char            tmp[BUFSIZ], *p, *t;
  1171.  
  1172.     p = buf;
  1173.     t = &tmp[0];
  1174.     l = 0;
  1175.  
  1176.     while ((*p != '\x00') && (l < max_len)) {
  1177.         if (*p != '\x09') {
  1178.             *t = *p;
  1179.             t++;
  1180.             p++;
  1181.             l++;
  1182.         }
  1183.         else {
  1184.             p++;
  1185.             k = ((l / 8) + 1) * 8;
  1186.             for (; l < k; l++) {
  1187.                 *t = ' ';
  1188.                 t++;
  1189.                 if (l >= max_len)
  1190.                     break;
  1191.             }
  1192.         }
  1193.     }
  1194.  
  1195.     *t = '\x00';
  1196.     strcpy(buf, tmp);
  1197. }
  1198.